package org.richin.servlet.util;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;



import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.richin.array.util.ArrayUtils;

public class CookieUtils {
	private static final Log log = LogFactory.getLog(CookieUtils.class);
	   /** 一年*/
    private static final int COOKIE_MAX_LIFE_TIME = 60 * 60 * 24 * 365;
    private static final int COOKIE_MAX_MONTH_TIME = 3600 * 24 * 30;
    private static final int COOKIE_MAX_DAY_TIME = 3600 * 24;
    private static final int COOKIE_MAX_BROWSER_TIME = -1;//负值表示当关闭浏览器时，该 cookie 将被清除，0 表示必须立即清除该 cookie
    private static final int COOKIE_MAX_ZERO_TIME = 0;

	/**
	 * Convenience method to get a cookie by name
	 * @param request request the current request
	 * @param name the name of the cookie to find
	 * @return the cookie (if found), null if not found
	 */
	public static Cookie getCookie(HttpServletRequest request, String name) {
		Cookie[] cookies = request.getCookies();
		Cookie returnCookie = null;
		if (cookies == null || StringUtils.isBlank(name)) {
			return returnCookie;
		}
		for (Cookie thisCookie : cookies) {
			if (thisCookie.getName().equals(name)) {
				returnCookie = thisCookie;
				break;
			}
		}
		return returnCookie;

	}

	public static String getCookieValue(HttpServletRequest request, String cookieName) {
		Cookie cookieList[] = request.getCookies();

		if (ArrayUtils.isEmpty(cookieList) || StringUtils.isEmpty(cookieName))
			return "";
		for (Cookie aCookieList : cookieList) {
			try {
				if (aCookieList.getName().equals(cookieName))
					return URLDecoder.decode(aCookieList.getValue(), "UTF8");
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		return "";
	}

	public static void setCookie(HttpServletResponse response, String cookieName, String cookieValue)
			throws UnsupportedEncodingException {
		Cookie theCookie = new Cookie(URLEncoder.encode(cookieName, "UTF8"), URLEncoder.encode(cookieValue, "UTF8"));
		response.addCookie(theCookie);
	}

	public static void setCookie(HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage,
			String domain) throws UnsupportedEncodingException {
		Cookie theCookie = new Cookie(URLEncoder.encode(cookieName, "UTF8"), URLEncoder.encode(cookieValue, "UTF8"));
		theCookie.setDomain(domain);
		theCookie.setPath("/");
		theCookie.setMaxAge(cookieMaxage);
		response.addCookie(theCookie);
	}
	/**
	 * 默认 Path为 "/"
	 * @param response
	 * @param cookieName
	 * @param cookieValue
	 * @param direction
	 * @throws UnsupportedEncodingException
	 */
	public static void setCookie(HttpServletResponse response, String cookieName, String cookieValue, Direction direction)
	throws UnsupportedEncodingException {
		Cookie theCookie = new Cookie(URLEncoder.encode(cookieName, "UTF8"), URLEncoder.encode(cookieValue, "UTF8"));
		theCookie.setPath("/");
		theCookie.setMaxAge(direction.value);
		response.addCookie(theCookie);
	}
	/**
	 * 添加默认浏览器进程 Path为 "/"的cookie
	 * @param response
	 * @param cookieName
	 * @param cookieValue
	 * @param direction
	 * @throws UnsupportedEncodingException
	 */
	public static void setBrowserCookie(HttpServletResponse response, String cookieName, String cookieValue)
	throws UnsupportedEncodingException {
		Cookie theCookie = new Cookie(URLEncoder.encode(cookieName, "UTF8"), URLEncoder.encode(cookieValue, "UTF8"));
		theCookie.setPath("/");
		theCookie.setMaxAge(Direction.Browser.value);
		response.addCookie(theCookie);
	}
	public static void setCookie(HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage)
			throws UnsupportedEncodingException {
		Cookie theCookie = new Cookie(URLEncoder.encode(cookieName, "UTF8"), URLEncoder.encode(cookieValue, "UTF8"));
		theCookie.setMaxAge(cookieMaxage);
		response.addCookie(theCookie);
	}

	/**
	 * Convenience method to set a cookie
	 * 
	 * @param response
	 *            the current response
	 * @param name
	 *            the name of the cookie
	 * @param value
	 *            the value of the cookie
	 * @param path
	 *            the path to set it on
	 * @throws UnsupportedEncodingException 
	 */
	public static void setCookie(HttpServletResponse response, String cookieName, String cookieValue, String path)
			throws UnsupportedEncodingException {
		log.debug("Setting cookie '" + cookieName + "' on path '" + path + "'");

		Cookie cookie = new Cookie(URLEncoder.encode(cookieName, "UTF8"), cookieValue == null ? null : URLEncoder
				.encode(cookieValue, "UTF8"));
		cookie.setSecure(false);
		if (StringUtils.isEmpty(path)) {
			path = "/";
		}
		cookie.setPath(path);
		cookie.setMaxAge(3600 * 24 * 30); // 30 days

		response.addCookie(cookie);
	}

	/**
	 * Convenience method for deleting a cookie by name
	 * 
	 * @param response
	 *            the current web response
	 * @param cookie
	 *            the cookie to delete
	 * @param path
	 *            the path on which the cookie was set (i.e. /appfuse)
	 */
	public static void deleteCookie(HttpServletResponse response, Cookie cookie, String path) {
		if (cookie != null) {
			// Delete the cookie by setting its maximum age to zero
			cookie.setMaxAge(0);
			cookie.setPath(path);
			response.addCookie(cookie);
		}
	}

	public static void deleteCookie(HttpServletResponse response, Cookie cookie, String domain, String path) {
		if (cookie != null) {
			cookie.setDomain(domain);
			cookie.setPath(path);
			cookie.setValue("");
			response.addCookie(cookie);
		}
	}

	public static void clearCookie(HttpServletRequest request, HttpServletResponse response, String key) {
		Cookie cookie = getCookie(request, key);
		if (cookie != null) {
			cookie.setValue(null);
			cookie.setMaxAge(0);
			cookie.setPath("/");
			response.addCookie(cookie);
		}
	}

	public static void setSessionCookie(HttpServletResponse response, String name, String value, String domain,
			String path) {
		if (value == null)
			value = "";
		Cookie cookie = new Cookie(name, value);
		cookie.setDomain(domain);
		cookie.setPath(path);
		response.addCookie(cookie);
	}

	public static void setCookie(HttpServletRequest request, HttpServletResponse response, String name, String value) {
		setCookie(request, response, name, value, 0x278d00);
	}

	public static void setCookie(HttpServletRequest request, HttpServletResponse response, String name, String value,
			int maxAge) {
		if (value == null)
			value = "";
		String path = request.getContextPath() != null ? request.getContextPath() : "/";
		if ("".equals(path))
			path = "/";
		Cookie cookie = new Cookie(name, value);
		//cookie.setMaxAge(maxAge);
		cookie.setPath(path);
		response.addCookie(cookie);
	}
	/**
     * Builds a cookie string containing a username and password.<p>
     *
     * Note: with open source this is not really secure, but it prevents users
     * from snooping the cookie file of others and by changing the XOR mask and
     * character offsets, you can easily tweak results.
     *
     * @param username The username.
     * @param password The password.
     * @return String encoding the input parameters, an empty string if one of
     *      the arguments equals <code>null</code>.
     */
    public static String encodePasswordCookie(String username, String password)
    {
        return encodePasswordCookie(username, password, new String(new char[]{DELIMITER, ENCODE_CHAR_OFFSET1, ENCODE_CHAR_OFFSET2}));
    }

	 /**
     * Builds a cookie string containing a username and password, using offsets to customise the encoding.<p>
     *
     * Note: with open source this is not really secure, but it prevents users
     * from snooping the cookie file of others and by changing the XOR mask and
     * character offsets, you can easily tweak results.
     *
     * @param username The username.
     * @param password The password.
     * @param encoding A String used to customise cookie encoding (only the first 3 characters are used)
     * @return String encoding the input parameters, an empty string if one of
     *      the arguments equals <code>null</code>.
     */
    public static String encodePasswordCookie(String username, String password, String encoding)
    {
        StringBuffer buf = new StringBuffer();
        if (username != null && password != null)
        {
            char offset1 = (encoding != null && encoding.length() > 1) ? encoding.charAt(1) : ENCODE_CHAR_OFFSET1;
            char offset2 = (encoding != null && encoding.length() > 2) ? encoding.charAt(2) : ENCODE_CHAR_OFFSET2;

            byte[] bytes = (username + DELIMITER + password).getBytes();
            int b;

            for (int n = 0; n < bytes.length; n++)
            {
                b = bytes[n] ^ (ENCODE_XORMASK + n);
                buf.append((char) (offset1 + (b & 0x0F)));
                buf.append((char) (offset2 + ((b >> 4) & 0x0F)));
            }
        }
        return buf.toString();
    }

    /**
     * Decodes a cookie string containing a username and password.
     * @param cookieVal The cookie value.
     * @return String[] containing the username at index 0 and the password at
     *      index 1, or <code>{ null, null }</code> if cookieVal equals
     *      <code>null</code> or the empty string.
     */
    public static String[] decodePasswordCookie(String cookieVal)
    {
        return decodePasswordCookie(cookieVal, new String(new char[]{DELIMITER, ENCODE_CHAR_OFFSET1, ENCODE_CHAR_OFFSET2}));
    }

    /**
     * Decodes a cookie string containing a username and password.
     * @param cookieVal The cookie value.
     * @param encoding  A String used to customise cookie encoding (only the first 3 characters are used) - should be the same string you used to encode the cookie!
     * @return String[] containing the username at index 0 and the password at
     *      index 1, or <code>{ null, null }</code> if cookieVal equals
     *      <code>null</code> or the empty string.
     */
    public static String[] decodePasswordCookie(String cookieVal, String encoding)
    {
        // check that the cookie value isn't null or zero-length
        if (cookieVal == null || cookieVal.length() <= 0)
        {
            return null;
        }

        char offset1 = (encoding != null && encoding.length() > 1) ? encoding.charAt(1) : ENCODE_CHAR_OFFSET1;
        char offset2 = (encoding != null && encoding.length() > 2) ? encoding.charAt(2) : ENCODE_CHAR_OFFSET2;

        // decode the cookie value
        char[] chars = cookieVal.toCharArray();
        byte[] bytes = new byte[chars.length / 2];
        int b;
        for (int n = 0, m = 0; n < bytes.length; n++)
        {
            b = chars[m++] - offset1;
            b |= (chars[m++] - offset2) << 4;
            bytes[n] = (byte) (b ^ (ENCODE_XORMASK + n));
        }
        cookieVal = new String(bytes);
        int pos = cookieVal.indexOf(DELIMITER);
        String username = (pos < 0) ? "" : cookieVal.substring(0, pos);
        String password = (pos < 0) ? "" : cookieVal.substring(pos + 1);

        return new String[]{username, password};
    }
    public static Map<String, Cookie> readCookieMap(HttpServletRequest request) {   
        Map<String, Cookie> cookieMap = new HashMap<String, Cookie>();   
        Cookie[] cookies = request.getCookies();   
        if (null != cookies) {   
            for (int i = 0; i < cookies.length; i++) {   
                cookieMap.put(cookies[i].getName(), cookies[i]);   
            }   
        }   
        return cookieMap;   
    }   

 // Character used to separate username and password in persistent cookies.
    // 0x13 == "Device Control 3" non-printing ASCII char. Unlikely to appear in a username
    private static final char DELIMITER = 0x13;

    //"Tweakable" parameters for the cookie encoding. NOTE: changing these
    //and recompiling this class will essentially invalidate old cookies.
    private final static int ENCODE_XORMASK = 0x5A;
    private final static char ENCODE_CHAR_OFFSET1 = 'C';
    private final static char ENCODE_CHAR_OFFSET2 = 'i';

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		String encodePasswordCookie = encodePasswordCookie("admin", "linknb","key");
		System.out.println(encodePasswordCookie);
		System.out.println(ArrayUtils.toString(decodePasswordCookie(encodePasswordCookie)));

	}
	public  enum Direction {
	    None(COOKIE_MAX_ZERO_TIME), Browser(COOKIE_MAX_BROWSER_TIME), Day (COOKIE_MAX_DAY_TIME), Month( COOKIE_MAX_MONTH_TIME), Year (COOKIE_MAX_LIFE_TIME);
	    
	    private final int value;
	    /**
	     * Constructor.
	     */
	    private Direction(int value) {
	        this.value = value;
	    }
	    
	    /**
	     * Get the value.
	     * @return the value
	     */
	    public int getValue() {
	        return value;
	    }
	}
}
